package com.ygl.gateway.bean;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;


@Component
public class CustomLoadBalancerClientFilter extends LoadBalancerClientFilter{

    /**
     * Filter order for {@link LoadBalancerClientFilter}.
     */
    public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;

    private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);

    @Autowired
    private  LoadBalancerClient loadBalancer;
    @Autowired
    private LoadBalancerProperties properties;

    public CustomLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
        super(loadBalancer, properties);
    }

    @Override
    public int getOrder() {
        return LOAD_BALANCER_CLIENT_FILTER_ORDER;
    }

    private String tenantId = "";

    @Override
    @SuppressWarnings("Duplicates")
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        tenantId = exchange.getAttribute("tenantId");
        URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
        if (url == null
                || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
            return chain.filter(exchange);
        }
        // preserve the original url
        addOriginalRequestUrl(exchange, url);

        if (log.isTraceEnabled()) {
            log.trace("customLoadBalancerClientFilter url before: " + url);
        }

        final ServiceInstance instance = choose(exchange);

        if (instance == null) {
            throw NotFoundException.create(properties.isUse404(),
                    "Unable to find instance for " + url.getHost());
        }

        URI uri = exchange.getRequest().getURI();

        // if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
        // if the loadbalancer doesn't provide one.
        String overrideScheme = instance.isSecure() ? "https" : "http";
        if (schemePrefix != null) {
            overrideScheme = url.getScheme();
        }

        URI requestUrl = loadBalancer.reconstructURI(
                new DelegatingServiceInstance(instance, overrideScheme), uri);

        if (log.isTraceEnabled()) {
            log.trace("customLoadBalancerClientFilter url chosen: " + requestUrl);
        }

        exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
        return chain.filter(exchange);
    }

//    @Autowired
//    private NacosMetadataLoadBalancerRule irule;

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;
    @Autowired
    private LoadBalancerClient loadBalancer1;

    private static final String TARGET_VERSION = "target-version";
    private static final String VERSION = "version";

    @Override
    protected ServiceInstance choose(ServerWebExchange exchange) {
        String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
        // 获取配置文件中所配置的集群名称
        String clusterName = discoveryProperties.getClusterName();
        // 获取配置文件中所配置的元数据
        String targetVersion = discoveryProperties.getMetadata().get(TARGET_VERSION);
        //想要请求的微服务的名称
        String clientName = serviceId;
        // 获取服务发现的相关API
        NamingService namingService = discoveryProperties.namingServiceInstance();
        try {
            // 获取该微服务的所有健康实例
            List<Instance> instances = namingService.selectInstances(clientName, true);
            List<Instance> metadataMatchInstances = instances;
            // 如果配置了版本映射，那么代表只调用元数据匹配的实例
            if (StringUtils.isNotBlank(tenantId)) {
                // 过滤与版本元数据相匹配的实例，以实现版本控制
                metadataMatchInstances = instances.stream().filter(i -> Objects.equals(tenantId, i.getMetadata().get("tenantId")))
                        .collect(Collectors.toList());

                if (CollectionUtils.isEmpty(metadataMatchInstances)) {
                    log.warn("未找到元数据匹配的目标实例！请检查配置。targetVersion = "+targetVersion+", instance = "+ instances);
                    return null;
                }
            }

            // 基于随机权重的负载均衡算法，从实例列表中选取一个实例
            Instance instance = ExtendBalancer.getHostByRandomWeight2(metadataMatchInstances);
            log.info("选择的实例是：port = "+instance.getPort()+", instance = "+ instance);
            NacosServer nacosServer = new NacosServer(instance);
            if (nacosServer != null) {
                return new RibbonLoadBalancerClient.RibbonServer(serviceId, nacosServer);
            }
        }catch (Exception e){
          log.info("xx");
        }

        return null;
//        return loadBalancer1.choose(serviceId);
//        Server choose = irule.choose(host);
//        return choose;
    }

}
